#pragma once

#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)


#define CREATER "creater"    
#define USER "user"

const int gshmid = -1;
const int gsize = 4096;
const char* pathname = ".";
const int projid = 666;
const int mode = 0666;

class Shm
{   
private:

    //服务端创建Shm
    void CreatHelper(int flag)
    {
        _key = ftok(pathname, projid);
        _shmid = shmget(_key, _size, flag);

        if(_shmid < 0)
        {
            ERR_EXIT("shmget");
        }else 
        {
            std::cout<<_usertype<<":"<<"创建成功！"<<std::endl;
        }
    }

    void Creat()
    {
        CreatHelper(IPC_CREAT | IPC_EXCL | mode);
    }


    //客户端打开Shm
    void Get()
    {
        CreatHelper(IPC_CREAT);
    }

    void Destory()
    {
        int n = shmctl(_shmid, IPC_RMID, nullptr);

        if(n < 0)
        {
            ERR_EXIT("~Shm");
        }else
        {
            std::cout<<"销毁成功"<<std::endl;
        }
    }

    void ShmAttach()
    {
        _mem = shmat(_shmid, nullptr, 0);
        if((long long)_mem < 0)
        {
            ERR_EXIT("shmat");
        }else 
        {
            std::cout<<"挂载成功！"<<std::endl;
        }
    }

    void Shmdt()
    {
        int ret = shmdt(_mem);

        if(ret < 0)
        {
            ERR_EXIT("shmdt");
        }
        else
        {
            std::cout<<"取消挂载成功！"<<std::endl;
        }
    }
    
public:    
    Shm(const std::string& usertype)
        : _shmid(gshmid), _size(gsize), _usertype(usertype)
    {
        if(_usertype == USER)
        {
            Creat();
        }
        else
        {
            Get();
        }

        ShmAttach();
    }

    void* VirtualAdd()
    {
        return _mem;
    }


    size_t Size()
    {
        return _size;
    }

    ~Shm()
    {
        std::cout<<_usertype<<std::endl;
        if(_usertype == CREATER)
            Destory();
    }
private:
    key_t _key;
    int _shmid;
    int _size;
    void* _mem;
    std::string _usertype;
};